package lz77;

import java.io.EOFException;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;


import lz77.util.ByteLinkedList;
import lz77.util.CharLinkedList;
import lz77.util.LinkedListNode;
import lz77.util.LoopArray;

public class Lz77 {
	/**
	 * Pakkaa tiedoston
	 * @param infile Pakattavan tiedoston nimi
	 * @param bufferSize Käytettävän puskurin koko
	 * @return Pakattu data
	 * @throws IOException
	 */
	public static CompressedData compress(String infile, char bufferSize) throws IOException {
		FileInputStream in = new FileInputStream(infile);
		Buffer b = new Buffer(in, bufferSize);

		CompressedData d = new CompressedData(bufferSize);
		while (true) {
	
			byte[] needle = b.getNextString(); 
			char[] result = b.kmp_find(needle);

			if (result[1] == 0) {
				d.add((char) 0);
				d.add(needle[0]);
			} else {

				int index = result[1] + b.bufferEnd;
				

				d.add(result[1]);
				d.add(result[0]);

				d.add(b.get(index));
			}

			try {
				for (int i = 0; (i <= result[1]); i++) {
					b.next();
				}
			} catch (EOFException e) {
				break; //Tiedosto on päättynyt
			}
		}
		in.close();
		return d;
	}
	
	/**
	 * Purkaa pakatun datan tiedostoon
	 * @param d Pakattu data
	 * @param outFile Tiedoston nimi
	 * @throws IOException
	 */
	public static void decompress(CompressedData d, String outFile)
			throws IOException {
		LinkedListNode node = d.head();
		LoopArray b = new LoopArray((char)(d.getSize()/2));

		FileOutputStream out = new FileOutputStream(outFile);

		while (node != null) {
			CharLinkedList lengthNode = null;
			lengthNode = (CharLinkedList) node;

			char length = lengthNode.value;
		
			if (length != 0) {
				node = node.next();
				CharLinkedList indexNode = (CharLinkedList) node;
				char index = indexNode.value;

				for (int i = 0; i < length; i++) {
					out.write(b.get(index));
					b.push(b.get(index));
				}
			}
			
			node = node.next();
			ByteLinkedList dataNode = (ByteLinkedList) node;
			b.push(dataNode.value);
			out.write(dataNode.value);	
			node = node.next();
		}
		out.flush();
		out.close();
	}
}
